Option Strict On
Option Explicit On

Imports System
Imports KadGen.MetadataWrapper
Imports KadGen.MetadataAdapter
Imports System.Text
Imports KadGen.Utility2005

Public Class Utility
   Public Const storedProcString As String = "SP"
   Public Const tableString As String = "Table"
   Public Const viewString As String = "View"
   Public Const criteriaString As String = "Criteria"


   Friend Shared Function GetCreateSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Return ResolveName(defaults.CreatePattern, objectDef.Name)
   End Function

   Friend Shared Function GetRetrieveSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Return ResolveName(defaults.RetrievePattern, objectDef.Name)
   End Function

   Friend Shared Function GetRetrieveChildSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef, _
            ByVal fKey As MapForeignKey) _
            As String
      ' 20070812 KAD Changed from TableName to Name in the following line
      ' 20070818 KAD Changed back to TableName in the following line
      Dim ret As String = ResolveName(defaults.RetrieveChildrenPattern, objectDef.UnderlyingTable)
      ret = ResolveName(ret, fKey.Name, "FKey")
      Return ret
   End Function

   Friend Shared Function GetRetrieveHierarchySPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Dim ret As String = ResolveName(defaults.RetrieveHierarchyPattern, objectDef.TableName)
      ret = ResolveName(ret, objectDef.Name)
      Return ret
   End Function

   Friend Shared Function GetRetrieveSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef, _
            ByVal criteria As MapSearchCriteria) _
            As String
      Return GetRetrieveSPName(defaults, objectDef) & Utility.ResolveName(defaults.RetrieveAlternatePattern, criteria.Name, criteriaString)
   End Function

   Friend Shared Function GetUpdateSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Return ResolveName(defaults.UpdatePattern, objectDef.Name)
   End Function

   Friend Shared Function GetDeleteSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Return ResolveName(defaults.DeletePattern, objectDef.Name)
   End Function

   Friend Shared Function GetSetSelectSPName( _
            ByVal defaults As Defaults, _
            ByVal objectDef As MapObjectDef) _
            As String
      Return ResolveName(defaults.SetSelectPattern, objectDef.Name)
   End Function

   Friend Shared Function GetSetSelectSPName( _
           ByVal defaults As Defaults, _
           ByVal objectDef As MapObjectDef, _
           ByVal criteria As MapSearchCriteria) _
           As String
      Return GetSetSelectSPName(defaults, objectDef) & Utility.ResolveName(defaults.RetrieveAlternatePattern, criteria.Name, criteriaString)
   End Function

   Public Shared Function ResolveName( _
             ByVal pattern As String, _
             ByVal objectName As String) _
             As String
      Return ResolveName(pattern, objectName, "Object")
   End Function

   Public Shared Function ResolveName( _
            ByVal pattern As String, _
            ByVal name As String, _
            ByVal token As String) _
            As String
      pattern = pattern.Replace("[" & token & "]", "{0}")
      Return String.Format(pattern, name)
   End Function

   Public Shared Function Morph( _
            ByVal sections As MorphSections, _
            ByVal spOrder As String, _
            ByVal tableOrder As String, _
            ByVal viewOrder As String, _
            ByVal dataStructureFileName As String, _
            ByVal ormMapFileName As String, _
            ByVal useSchemaInWhereClause As Boolean, _
            ByVal unbackedColumnBehavior As KadGen.CodeGenerationSupport.UnbackedColumnBehavior) _
            As IO.Stream
      Dim orders As List(Of Source) = GetOrder(spOrder, tableOrder, viewOrder)
      Dim defaults As New Defaults
      Dim stream As New IO.MemoryStream

      ' Prepare data and mapping
      Dim dataWrapper As DbsDataStructureCollection = Nothing
      If (sections And (MorphSections.Build Or MorphSections.Assembly Or MorphSections.DataStructure)) <> 0 Then
         dataWrapper = Dbs.GetWrapper(dataStructureFileName)
      End If
      Dim mapWrapper As MapMappingRoot = Nothing
      If (sections And (MorphSections.Build Or MorphSections.Assembly Or MorphSections.OrmMap)) <> 0 Then
         mapWrapper = Map.GetWrapper(ormMapFileName)
      End If
      defaults.SetMapDefaults(dataWrapper(0), mapWrapper.MapMappingRoot, orders, useSchemaInWhereClause, unbackedColumnBehavior)

      ' Build the Build section
      Dim morphToBuild As New MorphToBuild
      Dim actualBuild As BuildBuild = Nothing
      If (sections And MorphSections.Build) <> 0 Or (sections And MorphSections.Assembly) <> 0 Then
         actualBuild = morphToBuild.CreateBuild(dataWrapper(0), mapWrapper, defaults)
      Else
         Return stream
      End If

      ' Build the Assembly section
      Dim morphToAssembly As New MorphToAssembly
      Dim actualAssembly As GenAssembly = Nothing
      If (sections And MorphSections.Assembly) <> 0 Then
         actualAssembly = morphToAssembly.CreateAssembly(dataWrapper(0), mapWrapper, actualBuild, defaults)
      End If

      ' Write out to stream
      Dim settings As New Xml.XmlWriterSettings
      settings.Indent = True
      settings.NewLineOnAttributes = False
      Dim xmlWriter As Xml.XmlWriter = Xml.XmlWriter.Create(stream, settings)
      Map.StartMappingRoot(xmlWriter, mapWrapper)
      ' At present, this is too big to output
      'If (sections And MorphSections.OrmMap) <> 0 Then
      '   Map.OutputWrapperPart(xmlWriter, mapWrapper.MapMapping)
      'End If
      If (sections And MorphSections.Build) <> 0 Then
         Build.OutputWrapperPart(xmlWriter, actualBuild)
      End If
      If (sections And MorphSections.Assembly) <> 0 Then
         Gen.OutputWrapperPart(xmlWriter, actualAssembly)
      End If
      If (sections And MorphSections.DataStructure) <> 0 Then
         Dbs.OutputWrapperPart(xmlWriter, dataWrapper)
      End If
      Map.EndMappingRoot(xmlWriter, mapWrapper)
      xmlWriter.Flush()
      Return stream
   End Function

   Public Shared Function DefaultForTypeName(ByVal typeName As String) As String
      If KadGen.Utility2005.Tools.IsTypeNameNumeric(typeName) Then
         Return "0"
      End If
      Select Case typeName.ToLower()
         Case "system.boolean"
            Return "False"
         Case "system.guid"
            Return "Guid.NewGuid()"
         Case Else
            ' TODO: Need to deal with min as empty on smart date and other parametersparameter
            Return "New " & typeName & "()"
      End Select
   End Function

   Private Shared Function GetOrder( _
            ByVal spOrder As String, _
            ByVal tableOrder As String, _
            ByVal viewOrder As String) _
            As List(Of Source)
      ' Just brute force this. Add primary, then secondary, then tertiary and ignore ignores
      ' These strings come from the KadGen.CodeGenerationSupport.Order enum that is created from the XSD.
      ' It would be desirable to create a different design that allowed those enums to be available here.
      Dim ret As New List(Of Source)
      If spOrder.ToLower = "primary" Then
         ret.Add(Source.StoreProc)
      End If
      If tableOrder.ToLower = "primary" Then
         ret.Add(Source.Table)
      End If
      If viewOrder.ToLower = "primary" Then
         ret.Add(Source.View)
      End If
      If spOrder.ToLower = "secondary" Then
         ret.Add(Source.StoreProc)
      End If
      If tableOrder.ToLower = "secondary" Then
         ret.Add(Source.Table)
      End If
      If viewOrder.ToLower = "secondary" Then
         ret.Add(Source.View)
      End If
      If spOrder.ToLower = "tertiary" Then
         ret.Add(Source.StoreProc)
      End If
      If tableOrder.ToLower = "tertiary" Then
         ret.Add(Source.Table)
      End If
      If viewOrder.ToLower = "tertiary" Then
         ret.Add(Source.View)
      End If
      Return ret
   End Function

   Public Shared Function IsCriteriaPrimaryKey( _
            ByVal criteria As MapSearchCriteria, _
            ByVal objectDef As MapObjectDef) _
            As Boolean
      Dim pkeys As MapPropertyCollection = objectDef.PrimaryKeys
      ' Don't sweat the order
      If pkeys.Count <> criteria.MapSearchCriterions.Count Then
         Return False
      End If
      For Each criterion As MapSearchCriterion In criteria.MapSearchCriterions
         Dim bFound As Boolean = False
         For Each prop As MapProperty In pkeys
            If prop.ColumnName = criterion.ColumnName Then
               bFound = True
            End If
         Next
         If Not bFound Then
            Return False
         End If
      Next
      Return True
   End Function

   Public Shared Sub CopyCustomAttributes( _
         ByVal sourceWrapper As WrapperBase, _
         ByVal destWrapper As WrapperBase, _
         ByVal destRoot As IDocumentRoot)
      If sourceWrapper.Attributes IsNot Nothing Then
         For Each attribute As KadGen.MetadataWrapper.Attribute In sourceWrapper.Attributes
            If attribute.NamespacePrefix <> "xmlns" Then
               destRoot.AddNamespace(attribute.NamespaceUri, attribute.NamespacePrefix)
               destWrapper.AddCustomAttribute(attribute.NamespaceUri, attribute.NamespacePrefix, _
                        attribute.Name, attribute.Value)
            End If
         Next
      End If
   End Sub

   Public Shared Sub LogForUser(ByVal message As String)
      Console.WriteLine(message)
   End Sub

   Public Shared Function GetCasedName(ByVal name As String) As String
      If String.IsNullOrEmpty(name) Then
         Return name
      End If
      Dim ss As String = name.Substring(0, 1).ToLower(Globalization.CultureInfo.CurrentUICulture)
      Return ss & name.Substring(1)
   End Function

   Public Shared Function MakeAlias(ByVal name As String) As String
      ' Double underscores should be used for aliases only 
      Const aliasMarker As String = "__"
      Dim pos As Int32 = name.IndexOf(aliasMarker)
      Dim currentCount As Int32 = 0
      If pos >= 0 Then
         Dim aliasPiece As String = name.Substring(pos + 2)
         If aliasPiece.Length > 1 Then
            Throw New NotSupportedException("More than ten aliased references to the same file not supported")
         End If
         Dim ch As Char = Char.Parse(aliasPiece)
         If Char.IsNumber(ch) Then
            currentCount = CInt(Char.GetNumericValue(ch))
            name = name.Substring(0, pos)
         End If
      End If
      currentCount += 1
      name = name & aliasMarker & currentCount
      Return name
   End Function

   Private Shared mTraceSource As Diagnostics.TraceSource
   Private Shared ReadOnly Property TraceSource() As Diagnostics.TraceSource
      Get
         If mTraceSource Is Nothing Then
            mTraceSource = New Diagnostics.TraceSource("CreateMetadata2007")
         End If
         Return mTraceSource
      End Get
   End Property

   Public Shared Sub LogForUser( _
             ByVal message As String, _
             ByVal id As LogId, _
             ByVal ParamArray wrapperBases() As WrapperBase)
      Dim wrapperMessage As New WrapperMessage(message, Diagnostics.TraceEventType.Warning, id, wrapperBases)
      Console.WriteLine(wrapperMessage.ToString)
      wrapperMessage.Log(TraceSource)
   End Sub

   Public Shared Sub LogForUser(ByVal logMessage As LogMessage)
      LogForUser(logMessage.Message, logMessage.Id, logMessage.WrapperBases)
   End Sub

   Public Class LogMessage
      Private mMessage As String
      Private mId As LogId
      Private mWrapperBases() As WrapperBase

      Public Sub New( _
               ByVal message As String, _
               ByVal id As LogId, _
               ByVal ParamArray wrapperBases() As WrapperBase)
         mMessage = message
         mId = id
         mWrapperBases = wrapperBases
      End Sub

      Public ReadOnly Property Message() As String
         Get
            Return mMessage
         End Get
      End Property

      Public ReadOnly Property Id() As LogId
         Get
            Return Me.mId
         End Get
      End Property

      Public ReadOnly Property WrapperBases() As WrapperBase()
         Get
            Return Me.mWrapperBases
         End Get
      End Property

   End Class
End Class

